home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 November: Tool Chest / Dev.CD Nov 94.toast / Sample Code / Snippets / QuickDraw / Restore Screen Cluts / PictDocument.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-07-15  |  73.3 KB  |  2,032 lines  |  [TEXT/MPS ]

  1. /******************************************************************************\
  2. *
  3. * Apple Macintosh Developer Technical Support
  4. *
  5. * Picture Document routines
  6. *
  7. * Program: ColorReset
  8. * File:    PictDocument.c
  9. *
  10. * by:      Forrest Tanaka
  11. *
  12. * Copyright © 1988-1992 Apple Computer, Inc.
  13. * All rights reserved.
  14. *
  15. * This source file is the most important part of this program because it shows
  16. * all the code needed to maintain Picture Documents.  A Picture Document is a
  17. * window which displays an off-screen image and lets you paint into it, and
  18. * these two activities show how to use graphics environments to draw off screen
  19. * and copy the off-screen image to the window on the screen.
  20. *
  21. \******************************************************************************/
  22.  
  23.  
  24. /******************************************************************************\
  25. * Header Files
  26. \******************************************************************************/
  27.  
  28. /* System header files needed by MPW C */
  29. #ifndef THINK_C
  30. #include <Errors.h>
  31. #include <Menus.h>
  32. #include <Resources.h>
  33. #include <Windows.h>
  34. #endif
  35.  
  36. #include <GestaltEqu.h>
  37. #include <Palettes.h>
  38. #include <Picker.h>
  39. #include <QDOffscreen.h>
  40.  
  41. /* Header files specific to this program */
  42. #include "PictDocument.h"
  43. #include "EmergMem.h"
  44. #include "Exceptions.h"
  45. #include "ColorReset.h"
  46. #include "MenuHandler.h"
  47. #include "WindowPositioner.h"
  48.  
  49.  
  50. /******************************************************************************\
  51. * Private Constants
  52. \******************************************************************************/
  53.  
  54. /* Picture Document window constants */
  55. #define rPictDocWindID 128 /* Resource ID of Picture Document WIND resource */
  56. #define rScrollBarID   128 /* Resource ID of scroll bar CNTL resource */
  57. #define kDocWKind      128 /* windowKind field of Picture Document windows */
  58. #define kMinWindowSize 64  /* Minimum size of the window in pixels */
  59.  
  60. /* PICT file constants */
  61. #define kPictFileHeaderSize 512    /* Number of bytes in PICT file header */
  62. #define kFileCreator        'CLIM' /* Creator code for this application */
  63. #define kFileType           'PICT' /* File type for this app’s documents */
  64.  
  65. /* Off-screen Constants */
  66. #define kMaxRowBytes 0x3FFE     /* Maximum number of bytes in a row of pixels */
  67. #define kDefaultRes  0x00480000 /* Default resolution is 72 DPI; Fixed type */
  68. #define kITabRes     4          /* Inverse-table resolution */
  69.  
  70. /* Miscellaneous constants */
  71. #define kPaintRadius 8 /* Radius of a drop of paint in pixels */
  72.  
  73.  
  74. /******************************************************************************\
  75. * Private Types
  76. \******************************************************************************/
  77.  
  78. /* Picture Document information */
  79. typedef struct
  80. {
  81.     GWorldPtr     image;      /* Pointer to image in off-screen GWorld */
  82.     Rect          windowRect; /* Rectangle of window except for scroll bars */
  83.     Rect          destRect;   /* Rectangle of image in window */
  84.     RGBColor      foreground; /* Color of foreground */
  85.     RGBColor      background; /* Color of background */
  86.     ControlHandle vScrollBar; /* Horizontal scroll bar */
  87.     ControlHandle hScrollBar; /* Vertical scroll bar */
  88.     FSSpec        file;       /* File spec of Picture Document’s PICT file */
  89.     short         fileRef;    /* File ref # of Picture Document’s PICT file */
  90.     Boolean       dithering;  /* True if image should be dithered */
  91. } PictDocInfoRec, *PictDocInfoPtr, **PictDocInfoHnd;
  92.  
  93. /* Basic picture information */
  94. typedef struct
  95. {
  96.     CTabHandle colors; /* Handle to color table of deepest pixel map */
  97.     short      depth;  /* Max depth for all pixmaps (in the picture) */
  98.     Rect       frame;  /* Picture frame rect (contains entire picture) */
  99. } PictureInfoRec;
  100.  
  101.  
  102. /******************************************************************************\
  103. * Prototypes of Private Functions
  104. \******************************************************************************/
  105.  
  106. static OSErr CreatePictDoc(
  107.     Rect       *pictureRect,
  108.     short      pictureDepth,
  109.     CTabHandle pictureClut,
  110.     WindowPtr  *retDoc);
  111.  
  112. pascal void ScrollActionProc(
  113.     ControlHandle control,
  114.     short         part);
  115.  
  116. static void ScrollPictDoc(
  117.     WindowPtr docWindow,
  118.     short     hScrollDelta,
  119.     short     vScrollDelta);
  120.  
  121. void ResizePictDoc(
  122.     WindowPtr docWindow);
  123.  
  124. OSErr CreateOffScreen(
  125.     Rect       *bounds,
  126.     short      depth,
  127.     CTabHandle colors,
  128.     CGrafPtr   *retPort,
  129.     GDHandle   *retGDevice);
  130.  
  131. OSErr SetUpPixMap(
  132.     short        depth,
  133.     Rect         *bounds,
  134.     CTabHandle   colors,
  135.     short        bytesPerRow,
  136.     PixMapHandle aPixMap);
  137.  
  138. OSErr CreateGDevice(
  139.     PixMapHandle basePixMap,
  140.     GDHandle     *retGDevice);
  141.  
  142. void DisposeOffScreen(
  143.     CGrafPtr doomedPort,
  144.     GDHandle doomedGDevice);
  145.  
  146. OSErr GetPictureInfo(
  147.     short          pictFileRef,
  148.     PictureInfoRec *retPictInfo);
  149.  
  150. pascal void InfoBits(
  151.     BitMapPtr srcBits,
  152.     Rect      *srcRect,
  153.     Rect      *dstRect,
  154.     short     mode,
  155.     RgnHandle maskRgn);
  156.  
  157. OSErr DrawFilePicture(
  158.     short pictFileRef,
  159.     Rect  *destRect);
  160.  
  161. pascal void FileGetPic(
  162.     Ptr   dataPtr,
  163.     short byteCount);
  164.  
  165. OSErr OpenFilePicture(
  166.     short pictFileRef,
  167.     Rect  *sourceRect);
  168.  
  169. OSErr CloseFilePicture(void);
  170.  
  171. pascal void FilePutPic(
  172.     Ptr   dataPtr,
  173.     short byteCount);
  174.  
  175.  
  176. /******************************************************************************\
  177. * Global Variables
  178. \******************************************************************************/
  179.  
  180. PictureInfoRec gPictureInfo;  /* Information about a PICT file */
  181. PicHandle      gSpoolPicture; /* Temporary picture used to spool from PICT file */
  182. QDProcsPtr     gSavedProcs;   /* Saves existing QDProcs; used when spooling out */
  183. CQDProcs       gCustomProcs;  /* Customized bottlenecks to spool to PICT file */
  184. short          gPictFileRef;  /* File reference number of a PICT file */
  185. short          gPictureSize;  /* Size of picture in bytes */
  186.  
  187.  
  188. /******************************************************************************\
  189. * NAME & SYNOPSIS:
  190. * FindPictDoc: Find the Picture Document window for a specific file
  191. *
  192. * PARAMETERS:
  193. * See PictDocument.h
  194. *
  195. * DEFINITION:
  196. * See PictDocument.h
  197. *
  198. * DESCRIPTION:
  199. * Each Picture Document window in the window list is examined to see whether it
  200. * has a corresponding disk file.  The fileRef field of the Picture Document
  201. * information record is non-zero if the Picture Document has a disk file
  202. * associated with it.  If there is a disk file, then EqualFSSpec is called to
  203. * see if the FSSpec that I saved in the document’s info record is the same as
  204. * the one passed in fileSpec.  If they are, then a pointer to the window is
  205. * returned.  Otherwise, the loop goes to the next Picture Document window, if
  206. * there is one.
  207. *
  208. * See ColorReset.h for a definition of EqualFSSpec.
  209. *
  210. * RETURN VALUES:
  211. * See PictDocument.h
  212. \******************************************************************************/
  213.  
  214. WindowPtr FindPictDoc(
  215.     FSSpecPtr fileSpec) /* Specification of file to search for */
  216. {
  217.     WindowPtr      pictDocWindow; /* Pointer to Picture Document being tested */
  218.     PictDocInfoHnd pictDocInfo;   /* Handle to Picture Document info rec */
  219.     Boolean        foundDoc;      /* True if found Pict Doc w/ same FSSpec */
  220.  
  221.     /* Get a pointer to the first Picture Document window in the window list */
  222.     pictDocWindow = NextPictDocWindow( nil );
  223.  
  224.     /* Keep searching until found or at end of list */
  225.     foundDoc = false;
  226.     while (pictDocWindow != nil && !foundDoc)
  227.     {
  228.         /* Only have to check FSSpecs if document has a corresponding file */
  229.         pictDocInfo = (PictDocInfoHnd)GetWRefCon( pictDocWindow );
  230.         if ((**pictDocInfo).fileRef != 0)
  231.         {
  232.             /* See if the Picture Document’s file is same as fileSpec */
  233.             HLock( (Handle)pictDocInfo );
  234.             foundDoc = EqualFSSpec( fileSpec, &(**pictDocInfo).file );
  235.             HUnlock( (Handle)pictDocInfo );
  236.  
  237.             /* If FSSpecs aren’t the same, then go to next Picture Document */
  238.             if (!foundDoc)
  239.                 pictDocWindow = NextPictDocWindow( pictDocWindow );
  240.         }
  241.     }
  242.  
  243.     return pictDocWindow;
  244. }
  245.  
  246.  
  247. /******************************************************************************\
  248. * NAME & SYNOPSIS:
  249. * NextPictDocWindow: Return a pointer to the next Picture Document window
  250. *
  251. * PARAMETERS:
  252. * See PictDocument.h
  253. *
  254. * DEFINITION:
  255. * See PictDocument.h
  256. *
  257. * DESCRIPTION:
  258. * NextPictDocWindow goes through every window in the window list that’s behind
  259. * the window specified by aWindow until either a Picture Document window is
  260. * found or until the end of the window list is reached.  If aWindow is nil, then
  261. * the search starts with the front window.
  262. *
  263. * RETURN VALUES:
  264. * See PictDocument.h
  265. \******************************************************************************/
  266.  
  267. WindowPtr NextPictDocWindow(
  268.     WindowPtr aWindow) /* Window to start search from, or nil if front */
  269. {
  270.     /* Start search from next window, or front window if aWindow is nil */
  271.     if (aWindow == nil)
  272.         aWindow = FrontWindow();
  273.     else
  274.         aWindow = (WindowPtr)((WindowPeek)aWindow)->nextWindow;
  275.  
  276.     /* Search until Picture Document wind found or end of wind list reached */
  277.     while (aWindow != nil && !IsPictDocWindow( aWindow ))
  278.         aWindow = (WindowPtr)((WindowPeek)aWindow)->nextWindow;
  279.  
  280.     return aWindow;
  281. }
  282.  
  283.  
  284. /******************************************************************************\
  285. * NAME & SYNOPSIS:
  286. * IsPictDocWindow: Is a window a Picture Document window?
  287. *
  288. * PARAMETERS:
  289. * See PictDocument.h
  290. *
  291. * DEFINITION:
  292. * See PictDocument.h
  293. *
  294. * DESCRIPTION:
  295. * When I create a window, I store a code for that kind of window in the
  296. * windowKind field.  IsPictDocWindow checks the windowKind field of the given
  297. * window against the constant kDocWKind.  If they’re equal, then aWindow
  298. * must be a Picture Document window.
  299. *
  300. * RETURN VALUES:
  301. * See PictDocument.h
  302. \******************************************************************************/
  303.  
  304. Boolean IsPictDocWindow (
  305.     WindowPtr aWindow) /* Pointer to the window being tested */
  306. {
  307.     return ((WindowPeek)aWindow)->windowKind == kDocWKind;
  308. }
  309.  
  310.  
  311. /******************************************************************************\
  312. * NAME & SYNOPSIS:
  313. * DrawPictDoc: Draw the contents of a Picture Document
  314. *
  315. * PARAMETERS:
  316. * See PictDocument.h
  317. *
  318. * DEFINITION:
  319. * See PictDocument.h
  320. *
  321. * DESCRIPTION:
  322. * The image for Picture Documents is stored in an off-screen GrafPort, and its
  323. * GrafPtr is stored in the information record.  This GrafPtr is retrieved, and
  324. * it’s used to CopyBits the GrafPort to the window.  It’s possible that not all
  325. * of the image can be copied to the window.  For example, part of the window
  326. * might be covered by another window.  In this case, the visRgn of the window
  327. * takes care of clipping out the undesireable parts of the original image.  The
  328. * other case in which part of the image shouldn’t be drawn is if part of the
  329. * image is scrolled out of the window.  The visRgn of the window takes care of
  330. * part of this, but we still have to make sure that the image doesn’t draw
  331. * itself over the scroll bar areas of the window in case the scroll bars are
  332. * hidden, which they are if the window is inactive.  To take care of this, the
  333. * destination rectangle which is the rectangle that the off-screen image is
  334. * copied to, is first intersected with the window rectangle, which is the
  335. * rectangle of the window except for the scroll bars.  This is used as the
  336. * destination rectangle to CopyBits.  Then this rectangle is offset by the top-
  337. * left corner of the original destination rectangle, and this is used as the
  338. * source rectangle to CopyBits.  The off-screen image is then copied to the
  339. * window.
  340. *
  341. * RETURN VALUES:
  342. * See PictDocument.h
  343. \******************************************************************************/
  344.  
  345. void DrawPictDoc(
  346.     WindowPtr docWindow) /* Ptr to Picture Document window being drawn */
  347. {
  348.     PictDocInfoHnd pictDocInfo; /* Handle to Picture Document information rec */
  349.     GWorldPtr      image;       /* Pointer to the off-screen image */
  350.     Rect           destRect;    /* Rectangle to copy image into */
  351.     Rect           sourceRect;  /* Rectangle to copy image from */
  352.     Rect           windowRect;  /* Rect of the window except for scroll bars */
  353.     short          mode;        /* Drawing mode to use */
  354.  
  355.     /* Get a pointer to the off-screen image and the window rectangle */
  356.     pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
  357.     image = (**pictDocInfo).image;
  358.     windowRect = (**pictDocInfo).windowRect;
  359.  
  360.     /* Calculate the rectangle to copy the image to */
  361.     destRect = (**pictDocInfo).destRect;
  362.     (void)SectRect( &destRect, &windowRect, &destRect );
  363.  
  364.     /* Calculate the rectangle to copy the image from */
  365.     sourceRect = destRect;
  366.     OffsetRect( &sourceRect, -(**pictDocInfo).destRect.left,
  367.             -(**pictDocInfo).destRect.top );
  368.  
  369.     /* Specify the drawing mode to use */
  370.     mode = srcCopy;
  371.     if ((**pictDocInfo).dithering)
  372.         mode |= ditherCopy;
  373.  
  374.     /* Draw the off-screen image to the screen */
  375.     EraseRect( &docWindow->portRect );
  376.     CopyBits( &((GrafPtr)image)->portBits, &docWindow->portBits,
  377.             &sourceRect, &destRect,
  378.             mode, nil );
  379.  
  380.     /* Draw the grow box */
  381.     DrawGrowIcon( docWindow );
  382.  
  383.     /* Draw the scroll bars */
  384.     DrawControls( docWindow );
  385. }
  386.  
  387.  
  388. /******************************************************************************\
  389. * NAME & SYNOPSIS:
  390. * ClickPictDoc: Handle a mouse click in a Picture Document window
  391. *
  392. * PARAMETERS:
  393. * See PictDocument.h
  394. *
  395. * DEFINITION:
  396. * See PictDocument.h
  397. *
  398. * DESCRIPTION:
  399. * The first really important call in this routine is FindControl, which is the
  400. * toolbox call which tells you which control and which part of the control the
  401. * mouse was clicked in.  If it returns zero, then the mouse was clicked outside
  402. * of any control, and so we let the user paint into the document.
  403. *
  404. * Painting into the document is done within a loop which iterates as long as the
  405. * mouse button is held down.  While this is the case, the current mouse position
  406. * is retrieved with a call to GetMouse.  If the mouse position changed from the
  407. * previous iteration’s mouse position, then the mouse position is offset to take
  408. * into account the currently-scrolled position, and then the current port is set
  409. * to the document’s off-screen GrafPort.  Then, PaintOval is called which draws
  410. * a drop of paint into the off-screen GrafPort.
  411. *
  412. * The off-screen image then has to be copied to the window so that the user can
  413. * see the new drop of paint.  This is done by first seeing if the drop of paint
  414. * can actually be seen in the window in the currently-scrolled position.  If it
  415. * can, then the rectangle of the drop of paint is offset to take into account
  416. * the currently-scrolled position.  Then the current port is set to the document
  417. * window, and CopyBits is called to copy the new drop of paint to the document
  418. * window.
  419. *
  420. * Finally, the mouse position is remembered and the loop iterates again if the
  421. * mouse button is still held down.
  422. *
  423. * If FindControl indicates that the mouse click was in the thumb of a scroll
  424. * bar, TrackControl is called which lets the user move the thumb around until
  425. * the user releases the mouse button.  Then, the difference between the old and
  426. * new scroll bar position is calculated which indicates the number of pixels
  427. * that the document must be scrolled.  ScrollPictDoc, defined below, is then
  428. * called to scroll the document’s image, update the scrolled-in part of the
  429. * image, and update the document information record to reflect the new scrolled
  430. * position.
  431. *
  432. * If FindControl indicates that the mouse click was in the up arrow, down arrow,
  433. * page up, or page down areas of either scroll bar, then TrackControl is called
  434. * to scroll the document appropriately.  The ScrollActionProc routine, defined
  435. * below, handles the logistics of scrolling in this case.
  436. *
  437. * RETURN VALUES:
  438. * See PictDocument.h
  439. \******************************************************************************/
  440.  
  441. void ClickPictDoc(
  442.     WindowPtr   docWindow,   /* Picture Document window that was clicked */
  443.     EventRecord *clickEvent) /* Mouse click event */
  444. {
  445.     PictDocInfoHnd pictDocInfo;     /* Handle to Picture Document info rec */
  446.     GWorldPtr      image;           /* Pointer to the off-screen image */
  447.     GDHandle       savedDevice;     /* Saves default GDevice */
  448.     Rect           paintRect;       /* Rectangle to draw paint into */
  449.     Rect           windowPaintRect; /* Rectangle to draw paint into */
  450.     Rect           windowRect;      /* Window rect except for scroll bars */
  451.     Rect           destRect;        /* Rectangle of image in window */
  452.     Point          newPosition;     /* New pos of mouse click; local coords */
  453.     Point          oldPosition;     /* Old pos of mouse click; local coords */
  454.     ControlHandle  scrollBar;       /* Handle to the clicked scroll bar */
  455.     short          partNumber;      /* Part # of clicked part of control */
  456.     short          scrollDelta;     /* Number of pixels to scroll */
  457.  
  458.     savedDevice = GetGDevice();
  459.  
  460.     /* Get information about the Picture Document */
  461.     pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
  462.     image = (**pictDocInfo).image;
  463.     windowRect = (**pictDocInfo).windowRect;
  464.     destRect = (**pictDocInfo).destRect;
  465.  
  466.     /* Get the mouse click position */
  467.     SetPort( docWindow );
  468.     newPosition = clickEvent->where;
  469.     GlobalToLocal( /*◊*/&newPosition );
  470.  
  471.     /* Find whether the mouse was in a control */
  472.     partNumber = FindControl( newPosition, docWindow, /*<*/&scrollBar );
  473.     switch (partNumber)
  474.     {
  475.         case 0:
  476.             /* Clicked outside of any control; see if it was in the image */
  477.             if (PtInRect( newPosition, &windowRect ))
  478.             {
  479.                 /* Click in image; paint while mouse button is held down */
  480.                 oldPosition.v = oldPosition.h = -32767;
  481.                 while (StillDown())
  482.                 {
  483.                     /* Only do something if the mouse moved */
  484.                     if (!EqualPt( newPosition, oldPosition ))
  485.                     {
  486.                         /* Need to offset paint brush for scrolled position */
  487.                         SubPt( topLeft( destRect ), /*◊*/&newPosition );
  488.                         SetRect( /*<*/&paintRect, newPosition.h - kPaintRadius,
  489.                                 newPosition.v - kPaintRadius,
  490.                                 newPosition.h + kPaintRadius,
  491.                                 newPosition.v + kPaintRadius );
  492.  
  493.                         /* Drop some paint into the off-screen image */
  494.                         SetGWorld( image, nil );
  495.                         PaintOval( &paintRect );
  496.  
  497.                         /* Offset brush back to window position */
  498.                         AddPt( topLeft( destRect ), /*◊*/&newPosition );
  499.  
  500.                         /* Calc rect in window for to brush rect in image */
  501.                         windowPaintRect = paintRect;
  502.                         OffsetRect( &windowPaintRect, destRect.left,
  503.                                 destRect.top );
  504.  
  505.                         /* Only copy if some part of brush within image */
  506.                         if (SectRect( &windowPaintRect, &windowRect,
  507.                                 &windowPaintRect ))
  508.                         {
  509.                             /* Make clipped paint rect in off-screen image */
  510.                             paintRect = windowPaintRect;
  511.                             OffsetRect( &paintRect, -destRect.left,
  512.                                     -destRect.top );
  513.  
  514.                             /* Copy painted off-screen image to the screen */
  515.                             SetGWorld( (CGrafPtr)docWindow, nil );
  516.                             CopyBits( &((GrafPtr)image)->portBits, &docWindow->portBits,
  517.                                     &paintRect, &windowPaintRect,
  518.                                     srcCopy, nil );
  519.                         }
  520.  
  521.                         /* Remember the new mouse position */
  522.                         oldPosition = newPosition;
  523.                     }
  524.  
  525.                     /* Get the new position of the mouse */
  526.                     SetPort( docWindow );
  527.                     GetMouse( /*<*/&newPosition );
  528.                 }
  529.             }
  530.             break;
  531.         case inThumb:
  532.             /* Mouse in thumb of scroll bar; get existing control value */
  533.             scrollDelta = GetCtlValue( scrollBar );
  534.  
  535.             /* Track mouse in thumb until mouse button is released */
  536.             partNumber = TrackControl( scrollBar, newPosition, nil );
  537.             if (partNumber != 0)
  538.             {
  539.                 /* Find diff between old and new scroll bar values */
  540.                 scrollDelta -= GetCtlValue( scrollBar );
  541.                 if (scrollDelta != 0)
  542.                 {
  543.                     if (scrollBar == (**pictDocInfo).vScrollBar)
  544.                         ScrollPictDoc( docWindow, 0, scrollDelta );
  545.                     else
  546.                         ScrollPictDoc( docWindow, scrollDelta, 0 );
  547.                 }
  548.             }
  549.             break;
  550.         case inUpButton:
  551.         case inDownButton:
  552.         case inPageUp:
  553.         case inPageDown:
  554.             (void)TrackControl( scrollBar, newPosition, (ProcPtr)ScrollActionProc );
  555.             break;
  556.         default:
  557.             break;
  558.     }
  559. }
  560.  
  561.  
  562. /******************************************************************************\
  563. * NAME & SYNOPSIS:
  564. * ScrollActionProc: Handle a click in a scroll bar (except for thumb)
  565. *
  566. * PARAMETERS:
  567. * ControlHandle control: Control that received the mouse click
  568. * short         part:    Part number of the clicked control part
  569. *
  570. * DEFINITION:
  571. * ScrollActionProc is called by the Control Manager when TrackControl is called
  572. * in the ClickPictDoc function above.  It handles scrolling when the user clicks
  573. * in the scroll arrows or page areas of the scroll bar.  The document is
  574. * scrolled by the appropriate amount.
  575. *
  576. * DESCRIPTION:
  577. * ScrollActionProc first checks to see which part of the scroll bar was clicked.
  578. * If it was in either of the arrows, then the desired scroll amount is one
  579. * pixel.  If it was in either of the paging areas, then the desired scroll
  580. * amount is 32 pixels.  The current value of the clicked scroll bar is then
  581. * adjusted by the desired scroll amount, and the result is compared against the
  582. * limits of the scroll bar’s values.  If the new value is beyond either limit,
  583. * then it’s pinned back into the limits.  Then the real amount to scroll is
  584. * calculated simply by subtracting the new scroll value from the old scroll
  585. * value, and ScrollPictDoc (defined below) is called to scroll the Picture
  586. * Document window.
  587. *
  588. * RETURN VALUES:
  589. * None
  590. \******************************************************************************/
  591.  
  592. static pascal void ScrollActionProc(
  593.     ControlHandle control, /* Handle to clicked control */
  594.     short         part)    /* Part number of clicked control part */
  595. {
  596.     PictDocInfoHnd pictDocInfo; /* Handle to Picture Document info rec */
  597.     WindowPtr      docWindow;   /* Clicked Picture Document window */
  598.     Rect           windowRect;  /* Window rect except for scroll bars */
  599.     Rect           destRect;    /* Rectangle of image in window */
  600.     short          scrollDelta; /* Number of pixels to scroll by */
  601.     short          oldValue;    /* Value of scroll bar before scrolling */
  602.     short          newValue;    /* Value of scroll bar after scrolling */
  603.     short          maxValue;    /* Maximum value of the scroll bar */
  604.  
  605.     /* Get information about the Picture Document */
  606.     docWindow = (**control).contrlOwner;
  607.     pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
  608.     windowRect = (**pictDocInfo).windowRect;
  609.     destRect = (**pictDocInfo).destRect;
  610.  
  611.     /* Only process if the part # isn’t zero */
  612.     if (part != 0)
  613.     {
  614.         /* Decide scroll amount based on part of control that was clicked */
  615.         switch (part)
  616.         {
  617.             case inUpButton:
  618.                 scrollDelta = 1;
  619.                 break;
  620.             case inDownButton:
  621.                 scrollDelta = -1;
  622.                 break;
  623.             case inPageUp:
  624.                 scrollDelta = 32;
  625.                 break;
  626.             case inPageDown:
  627.                 scrollDelta = -32;
  628.                 break;
  629.             default:
  630.                 scrollDelta = 0;
  631.                 break;
  632.         }
  633.  
  634.         /* Only do something if we’re actually scrolling */
  635.         if (scrollDelta != 0)
  636.         {
  637.             /* Get the current scroll bar state */
  638.             maxValue = GetCtlMax( control );
  639.             oldValue = GetCtlValue( control );
  640.  
  641.             /* Calculate the new scroll bar value pinned to the limits */
  642.             newValue = oldValue - scrollDelta;
  643.             if (newValue < 0)
  644.                 newValue = 0;
  645.             else if (newValue > maxValue)
  646.                 newValue = maxValue;
  647.  
  648.             /* Only scroll if the old and new values are different */
  649.             if (oldValue != newValue)
  650.             {
  651.                 /* Set the new value of the scroll bar */
  652.                 SetCtlValue( control, newValue );
  653.  
  654.                 /* Scroll by difference between old and new scroll bar values */
  655.                 scrollDelta = oldValue - newValue;
  656.                 if (control == (**pictDocInfo).vScrollBar)
  657.                     ScrollPictDoc( docWindow, 0, scrollDelta );
  658.                 else
  659.                     ScrollPictDoc( docWindow, scrollDelta, 0 );
  660.             }
  661.         }
  662.     }
  663. }
  664.  
  665.  
  666. /******************************************************************************\
  667. * NAME & SYNOPSIS:
  668. * ScrollPictDoc: Scroll a Picture Document window’s image
  669. *
  670. * PARAMETERS:
  671. * WindowPtr docWindow:    Pointer to Picture Document window to scroll
  672. * short     vScrollDelta: Number of pixels to scroll vertically
  673. * short     hScrollDelta: Number of pixels to scroll horizontally
  674. *
  675. * DEFINITION:
  676. * ScrollPictDoc scrolls the picture document window specified by the docWindow
  677. * parameter by vScrollDelta pixels vertically and hScrollDelta pixels
  678. * horizontally.  The part of the window that’s scrolled in is updated, so no
  679. * updating is needed outside of ScrollPictDoc.
  680. *
  681. * DESCRIPTION:
  682. * ScrollRect is called to scroll the Picture Document’s image by the specified
  683. * amount.  The scrolled-in part is then redrawn with a call to CopyBits.  The
  684. * destRect field of the Picture Document information record keeps track of the
  685. * scrolled position of the image, so it’s updated to reflect the new scrolled
  686. * position.
  687. *
  688. * RETURN VALUES:
  689. * None
  690. \******************************************************************************/
  691.  
  692. static void ScrollPictDoc(
  693.     WindowPtr docWindow,    /* Pointer to document window to scroll */
  694.     short     hScrollDelta, /* Number of pixels to scroll horizontally */
  695.     short     vScrollDelta) /* Number of pixels to scroll vertically */
  696. {
  697.     PictDocInfoHnd pictDocInfo;  /* Handle to Picture Document info rec */
  698.     RgnHandle      updateRegion; /* Region covering scrolled-in area */
  699.     Rect           windowRect;   /* Rect of window except for scroll bars */
  700.     Rect           destRect;     /* New destination rectangle */
  701.     GWorldPtr      image;        /* Off-screen document image */
  702.  
  703.     /* Get the window rectangle and destination rectangle */
  704.     pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
  705.     windowRect = (**pictDocInfo).windowRect;
  706.     destRect = (**pictDocInfo).destRect;
  707.     image = (**pictDocInfo).image;
  708.  
  709.     /* Scroll the image by the specified amount */
  710.     updateRegion = NewRgn();
  711.     ScrollRect( &windowRect, hScrollDelta, vScrollDelta, updateRegion );
  712.  
  713.     /* Update the destination rectangle to the new scrolled position */
  714.     OffsetRect( &destRect, hScrollDelta, vScrollDelta );
  715.     (**pictDocInfo).destRect = destRect;
  716.  
  717.     /* Update the scrolled-in part of the window */
  718.     CopyBits( &((GrafPtr)image)->portBits, &docWindow->portBits,
  719.             &image->portRect, &destRect,
  720.             srcCopy, updateRegion );
  721.  
  722.     /* Get rid of the update region */
  723.     DisposeRgn( updateRegion );
  724. }
  725.  
  726.  
  727. /******************************************************************************\
  728. * NAME & SYNOPSIS:
  729. * GrowPictDoc: Let the user change the size of a Picture Document window
  730. *
  731. * PARAMETERS:
  732. * See PictDocument.h
  733. *
  734. * DEFINITION:
  735. * See PictDocument.h
  736. *
  737. * DESCRIPTION:
  738. * GrowWindow is called with a boundary rectangle that specifies that the window
  739. * has a minimum size of kMinWindowSize (defined at the top of this source file)
  740. * and a maximum size that’s the size of the image.  After the user releases the
  741. * mouse button, SizeWindow is called to change the size of the window, and
  742. * ResizePictDoc (defined below) is called to update the Picture Document to the
  743. * new size.
  744. *
  745. * RETURN VALUES:
  746. * See PictDocument.h
  747. \******************************************************************************/
  748.  
  749. void GrowPictDoc(
  750.     WindowPtr   docWindow,   /* Picture Document window that’s to be grown */
  751.     EventRecord *clickEvent) /* Mouse down event */
  752. {
  753.     PictDocInfoHnd pictDocInfo; /* Handle to Picture Document info rec */
  754.     Rect           growBounds;  /* Window can be dragged over this rectangle */
  755.     Point          newSize;     /* New size of the new window */
  756.  
  757.     pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
  758.  
  759.     growBounds = (**pictDocInfo).image->portRect;
  760.     growBounds.left = growBounds.top = kMinWindowSize;
  761.     growBounds.right += kScrollBarWidth;
  762.     growBounds.bottom += kScrollBarWidth;
  763.  
  764.     /* Let the user resize the window to any size */
  765.     *((long *)&newSize) = GrowWindow( docWindow, clickEvent->where,
  766.             &growBounds );
  767.  
  768.     /* If the user actually changed the size of the window, resize the window */
  769.     if (*((long *)&newSize) != 0)
  770.     {
  771.         SizeWindow( docWindow, newSize.h, newSize.v, true );
  772.         ResizePictDoc( docWindow );
  773.     }
  774. }
  775.  
  776.  
  777. /******************************************************************************\
  778. * NAME & SYNOPSIS:
  779. * ResizePictDoc: Update a picture document window to a new size
  780. *
  781. * PARAMETERS:
  782. * WindowPtr docWindow: Picture document window that was resized
  783. *
  784. * DEFINITION:
  785. * ResizePictDoc updates the Picture Document window specified by the docWindow
  786. * to a new size.  This routine is called right after the size of docWindow has
  787. * been changed.  The act of updating the Picture Document window involves moving
  788. * the scroll bars to the edge of the window and scrolling the document image.
  789. * The image has to be scrolled when the window is grown in case the bottom or
  790. * right edge of the window goes below or to the right of the image, in which
  791. * case the bottom or right edges of the image has to “chase” the bottom or right
  792. * edges of the window, and that means scrolling.
  793. *
  794. * DESCRIPTION:
  795. * The first major thing that’s done is a new destination rectangle is calculated
  796. * in case the document has to be scrolled as described in the definition.  There
  797. * are three conditions that have to be met before scrolling can happen:
  798. *     1) The upper-left corner of destRect must not be (0,0), meaning that the
  799. *        document’s image is scrolled from its original position.
  800. *     2) The window has become bigger.
  801. *     3) The window’s rectangle has moved below or to the right of the image in
  802. *        the window.
  803. * If all three of these conditions are met for either the horizontal or
  804. * vertical cases, a new destRect is calculated.  If this new destRect is
  805. * different from the old destRect, ScrollPictDoc (defined above) is called to
  806. * scroll the image to the new position.
  807. *
  808. * After this, the windowRect field of the Picture Document information record
  809. * is updated to the new size of the window.  Then, the maximum values of the
  810. * scroll bars are set to the number of pixels in the horizontal or vertical
  811. * direction that can’t be seen.  And then, the old position of the scroll icon
  812. * is invalidated so that it’ll be erased with the document’s image the next time
  813. * the window is updated.
  814. *
  815. * Both of the scroll bars are then moved along the bottom and right edges of the
  816. * window, and the new position of the grow icon is invalidated so that it’s
  817. * redrawn in case the window was made smaller.  Finally, the rectangles of the
  818. * scroll bars are validated so that they won’t be redrawn in the next update
  819. * event.
  820. *
  821. * RETURN VALUES:
  822. * None
  823. \******************************************************************************/
  824.  
  825. static void ResizePictDoc(
  826.     WindowPtr docWindow) /* Picture document window that was resized */
  827. {
  828.     PictDocInfoHnd pictDocInfo;   /* Handle to Picture Document information rec */
  829.     ControlHandle  vScrollBar;    /* Vertical scroll bar */
  830.     ControlHandle  hScrollBar;    /* Horizontal scroll bar */
  831.     Rect           windowRect;    /* Covers part of window that contains image */
  832.     Rect           newWindowRect; /* Covers all of window except scroll bars */
  833.     Rect           scrollRect;    /* Rectangle of scroll bar */
  834.     Rect           destRect;      /* Rectangle of to draw image into */
  835.     Rect           newDestRect;   /* Rectangle of to draw image into */
  836.     CGrafPtr       image;         /* Off-screen document image */
  837.     short          remaining;     /* # rows, columns of pixels not visible */
  838.  
  839.     SetPort( docWindow );
  840.  
  841.     /* Get the Picture Document’s information */
  842.     pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
  843.     vScrollBar = (**pictDocInfo).vScrollBar;
  844.     hScrollBar = (**pictDocInfo).hScrollBar;
  845.     image = (**pictDocInfo).image;
  846.     destRect = (**pictDocInfo).destRect;
  847.     windowRect = (**pictDocInfo).windowRect;
  848.  
  849.     /* Find the new window document area */
  850.     newWindowRect = docWindow->portRect;
  851.     newWindowRect.right -= kScrollBarWidth - 1;
  852.     newWindowRect.bottom -= kScrollBarWidth - 1;
  853.  
  854.     /* Find new destRect in case window growing causes scrolling */
  855.     newDestRect = destRect;
  856.     if (newDestRect.left != 0 && 
  857.             (newWindowRect.right - newWindowRect.left) >
  858.             (windowRect.right - windowRect.left) &&
  859.             newWindowRect.right > newDestRect.right)
  860.         OffsetRect( /*◊*/&newDestRect, (newWindowRect.right - windowRect.right)
  861.                 - (newDestRect.right - windowRect.right), 0 );
  862.     if (newDestRect.top != 0 && 
  863.             (newWindowRect.bottom - newWindowRect.top) >
  864.             (windowRect.bottom - windowRect.top) &&
  865.             newWindowRect.bottom > newDestRect.bottom)
  866.         OffsetRect( /*◊*/&newDestRect, 0, (newWindowRect.bottom -
  867.                 windowRect.bottom) - (newDestRect.bottom - windowRect.bottom) );
  868.  
  869.     /* If the new scrolled position has changed, update the window’s image */
  870.     if (!EqualRect( &newDestRect, &destRect))
  871.         ScrollPictDoc( docWindow, newDestRect.left - destRect.left,
  872.                 newDestRect.top - destRect.top );
  873.  
  874.     /* Set up the rectangles for scroll bar calculations */
  875.     (**pictDocInfo).windowRect = newWindowRect;
  876.  
  877.     /* Calc # rows of image not visible; set as max value of scroll bar */
  878.     remaining = image->portRect.bottom - newWindowRect.bottom;
  879.     if (remaining < 0)
  880.         remaining = 0;
  881.     SetCtlMax( vScrollBar, remaining );
  882.  
  883.     /* Calc # cols of image not visible; set as max value of scroll bar */
  884.     remaining = image->portRect.right - newWindowRect.right;
  885.     if (remaining < 0)
  886.         remaining = 0;
  887.     SetCtlMax( hScrollBar, remaining );
  888.  
  889.     /* Invalidate the old position of the grow icon */
  890.     SetRect( &scrollRect, (**hScrollBar).contrlRect.right,
  891.             (**vScrollBar).contrlRect.bottom,
  892.             (**vScrollBar).contrlRect.right,
  893.             (**hScrollBar).contrlRect.bottom );
  894.     InvalRect( &scrollRect );
  895.  
  896.     /* Move the scroll bars along the edges of the window */
  897.     HideControl( vScrollBar );
  898.     HideControl( hScrollBar );
  899.     MoveControl( vScrollBar, newWindowRect.right, newWindowRect.top - 1 );
  900.     MoveControl( hScrollBar, newWindowRect.left - 1, newWindowRect.bottom );
  901.     SizeControl( vScrollBar, kScrollBarWidth, newWindowRect.bottom -
  902.             newWindowRect.top + 2 );
  903.     SizeControl( hScrollBar, newWindowRect.right - newWindowRect.left + 2,
  904.             kScrollBarWidth );
  905.     ShowControl( vScrollBar );
  906.     ShowControl( hScrollBar );
  907.  
  908.     /* Invalidate the new position of the grow icon */
  909.     SetRect( &scrollRect, (**hScrollBar).contrlRect.right,
  910.             (**vScrollBar).contrlRect.bottom,
  911.             (**vScrollBar).contrlRect.right,
  912.             (**hScrollBar).contrlRect.bottom );
  913.     InvalRect( &scrollRect );
  914.  
  915.     /* Validate the new scroll bar areas */
  916.     scrollRect = (**vScrollBar).contrlRect;
  917.     ValidRect( &scrollRect );
  918.     scrollRect = (**hScrollBar).contrlRect;
  919.     ValidRect( &scrollRect );
  920. }
  921.  
  922.  
  923. /******************************************************************************\
  924. * NAME & SYNOPSIS:
  925. * ActivatePictDoc: Activate or deactivate a Picture Document window
  926. *
  927. * PARAMETERS:
  928. * See PictDocument.h
  929. *
  930. * DEFINITION:
  931. * See PictDocument.h
  932. *
  933. * DESCRIPTION:
  934. * If the Picture Document window is becoming active, the scroll bars are shown.
  935. * If it’s becoming inactive, the scroll bars are hidden.  Because hiding
  936. * controls causes the control rectangle to be invalidated, ActivatePictDoc
  937. * validates them again to avoid a very slight flicker in the lines that delimit
  938. * the scroll bar areas.
  939. *
  940. * DrawGrowIcon is called to show or hide the grow icon.  The Window Manager
  941. * automatically decides whether to show or hide the grow icon.
  942. *
  943. * RETURN VALUES:
  944. * See PictDocument.h
  945. \******************************************************************************/
  946.  
  947. void ActivatePictDoc(
  948.     WindowPtr docWindow,      /* Pointer to Doc window being updated */
  949.     Boolean   becomingActive) /* True if Doc wind is becoming active */
  950. {
  951.     PictDocInfoHnd pictDocInfo; /* Handle to Picture Document information rec */
  952.     ControlHandle  vScrollBar;  /* Vertical scroll bar */
  953.     ControlHandle  hScrollBar;  /* Horizontal scroll bar */
  954.     Rect           scrollRect;  /* Rectangle of the scroll bars */
  955.  
  956.     /* Get the scroll bars of the window */
  957.     pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
  958.     vScrollBar = (**pictDocInfo).vScrollBar;
  959.     hScrollBar = (**pictDocInfo).hScrollBar;
  960.  
  961.     /* Show scroll bars if becoming active; hide if becoming inactive */
  962.     if (becomingActive)
  963.     {
  964.         ShowControl( vScrollBar );
  965.         ShowControl( hScrollBar );
  966.     }
  967.     else
  968.     {
  969.         HideControl( vScrollBar );
  970.         HideControl( hScrollBar );
  971.  
  972.         /* To avoid a slight flicker, validate the scroll bar rectangles */
  973.         SetPort( docWindow );
  974.         scrollRect = (**vScrollBar).contrlRect;
  975.         ValidRect( &scrollRect );
  976.         scrollRect = (**hScrollBar).contrlRect;
  977.         ValidRect( &scrollRect );
  978.     }
  979.  
  980.     /* Show or hide the grow icon */
  981.     DrawGrowIcon( docWindow );
  982. }
  983.  
  984.  
  985. /******************************************************************************\
  986. * NAME & SYNOPSIS:
  987. * FixPictDocMenus: Dim, undim, check, or uncheck menus for Picture Documents
  988. *
  989. * PARAMETERS:
  990. * See PictDocument.h
  991. *
  992. * DEFINITION:
  993. * See PictDocument.h
  994. *
  995. * DESCRIPTION:
  996. * The Close and Save As… items in the File menu are enabled when a Picture
  997. * Document window is active, as is the Choose Color… item in the Colors menu.
  998. *
  999. * RETURN VALUES:
  1000. * See PictDocument.h
  1001. \******************************************************************************/
  1002.  
  1003. void FixPictDocMenus(
  1004.     WindowPtr docWindow) /* Pointer to active Picture Document window */
  1005. {
  1006. #pragma unused(docWindow)
  1007.     MenuHandle aMenu; /* Handle to the menus being udpated */
  1008.  
  1009.     /* Set items in the File menu */
  1010.     aMenu = GetMHandle( mFile );
  1011.     EnableItem( aMenu, iClose );
  1012.     EnableItem( aMenu, iSaveAs );
  1013. }
  1014.  
  1015.  
  1016. /******************************************************************************\
  1017. * NAME & SYNOPSIS:
  1018. * DoOpenPictDoc: Open a new Picture Document
  1019. *
  1020. * PARAMETERS:
  1021. * See PictDocument.h
  1022. *
  1023. * DEFINITION:
  1024. * See PictDocument.h
  1025. *
  1026. * DESCRIPTION:
  1027. * The usual Standard File dialog is used to get the name and location of the
  1028. * PICT file to open.  Then FindPictDoc (defined earlier in this file) is called
  1029. * to determine whether the chosen PICT file is already open within this
  1030. * application.  If it is, then that PICT file’s window is just selected and
  1031. * DoOpenPictDoc returns with a pointer to that window.
  1032. *
  1033. * If the file isn’t open, then it’s opened here with HOpenDF.  This routine is
  1034. * used because it works even on systems that don’t have the FSSpec-based File
  1035. * Manager calls.  GetPictureRect (defined later in this file) is called to get
  1036. * the picFrame from the picture in the chosen PICT file.  Once this is done,
  1037. * CreatePictDoc is called to create a blank Picture Document window that’s ready
  1038. * to read the image into.
  1039. *
  1040. * The current port is set to the off-screen GrafPort that CreatePictDoc created,
  1041. * and then DrawFilePicture (defined later in this file) is called to spool the
  1042. * PICT file into the current port.
  1043. *
  1044. * RETURN VALUES:
  1045. * See PictDocument.h
  1046. \******************************************************************************/
  1047.  
  1048. WindowPtr DoOpenPictDoc()
  1049. {
  1050.     WindowPtr         pictDocWindow;  /* Pointer to new Picture Doc window */
  1051.     PictDocInfoHnd    pictDocInfo;    /* Handle to Picture Document info rec */
  1052.     PictureInfoRec    pictureInfo;    /* PixMap information from PICT file */
  1053.     Rect              pictureRect;    /* Rectangle of picture in PICT file */
  1054.     CGrafPtr          newImage;       /* New off-screen image */
  1055.     RGBColor          color;          /* Use to set default fore/back colors */
  1056.     CGrafPtr          savedPort;      /* Ptr to current port for restoring */
  1057.     GDHandle          savedDevice;    /* Current GDevice for restoring */
  1058.     StandardFileReply reply;          /* User’s choice for PICT file to open */
  1059.     SFTypeList        typeList;       /* Specifies we can open 'PICT' files */
  1060.     short             pictFileRef;    /* File reference number of PICT file */
  1061.     OSErr             error;
  1062.     ExceptionRec      exception;
  1063.     long              msgType;
  1064.     long              msgCode;
  1065.  
  1066.     pictDocWindow = nil;
  1067.     pictFileRef = 0;
  1068.  
  1069.     /* Exception-handling section */
  1070.     if (ExceptionEntry( /*<*/&exception, /*<*/&msgType, /*<*/&msgCode ))
  1071.     {
  1072.         if (pictFileRef != 0)
  1073.             (void)FSClose( pictFileRef );
  1074.         if (pictDocWindow != nil)
  1075.             DoClosePictDoc( pictDocWindow );
  1076.         (void)ShowAlert( kStopAlert, rOKAlertID, msgType, msgCode );
  1077.         return nil;
  1078.     }
  1079.  
  1080.     /* Let the user choose a PICT file */
  1081.     typeList [0] = 'PICT';
  1082.     if (FileSpecGet( nil, 1, typeList, /*<*/&reply ))
  1083.     {
  1084.         /* First determine whether chosen file is already open in this app */
  1085.         pictDocWindow = FindPictDoc( &reply.sfFile );
  1086.         if (pictDocWindow == nil)
  1087.         {
  1088.             /* Open the PICT file */
  1089.             error = HOpenDF( reply.sfFile.vRefNum, reply.sfFile.parID,
  1090.                     reply.sfFile.name, fsCurPerm, /*<*/&pictFileRef );
  1091.             if (error != noErr)
  1092.                 if (error == opWrErr)
  1093.                     Exception( &exception, rFileErrMessages,
  1094.                             kFileErrDocOpenMsg );
  1095.                 else
  1096.                     Exception( &exception, rMiscErrMessages,
  1097.                             kMiscErrUnknownMsg );
  1098.  
  1099.             /* Get interesting information about a picture */
  1100.             error = GetPictureInfo( pictFileRef, /*<*/&pictureInfo );
  1101.             if (error != noErr)
  1102.                 Exception( &exception, rMiscErrMessages, kMiscErrUnknownMsg );
  1103.             pictureRect = pictureInfo.frame;
  1104.             OffsetRect( /*◊*/&pictureRect, -pictureRect.left,
  1105.                     -pictureRect.top );
  1106.  
  1107.             /* Open a new Picture Document window*/
  1108.             error = CreatePictDoc( &pictureRect, pictureInfo.depth,
  1109.                     pictureInfo.colors, /*<*/&pictDocWindow );
  1110.             if (error == memFullErr)
  1111.                 Exception( &exception, rMemErrMessages, kMemErrOpenDocMsg );
  1112.             else if (error == resNotFound)
  1113.                 Exception( &exception, rResErrMessages, kResErrAppDamageMsg );
  1114.             else if (error != noErr)
  1115.                 Exception( &exception, rMiscErrMessages, kMiscErrUnknownMsg );
  1116.  
  1117.             /* Fill in the Picture Document information */
  1118.             pictDocInfo = (PictDocInfoHnd)GetWRefCon( pictDocWindow );
  1119.             color.red = color.green = color.blue = 0x0000;
  1120.             (**pictDocInfo).foreground = color;
  1121.             color.red = color.green = color.blue = 0xFFFF;
  1122.             (**pictDocInfo).background = color;
  1123.             (**pictDocInfo).fileRef = pictFileRef;
  1124.             (**pictDocInfo).file = reply.sfFile;
  1125.             (**pictDocInfo).dithering = false;
  1126.  
  1127.             /* Get a handle to the Picture Document information record */
  1128.             pictDocInfo = (PictDocInfoHnd)GetWRefCon( pictDocWindow );
  1129.  
  1130.             /* Save current port and set newImage as current port */
  1131.             newImage = (**pictDocInfo).image;
  1132.             GetGWorld( /*<*/&savedPort, /*<*/&savedDevice );
  1133.             SetGWorld( newImage, nil );
  1134.  
  1135.             /* Draw the contents of the PICT file into the current port */
  1136.             error = DrawFilePicture( pictFileRef, &newImage->portRect );
  1137.  
  1138.             /* Restore the current port and check for errors */
  1139.             SetGWorld( savedPort, savedDevice );
  1140.             if (error != noErr)
  1141.                 Exception( &exception, rMiscErrMessages, kMiscErrUnknownMsg );
  1142.  
  1143.             /* Set the title of the window to the title of the file */
  1144.             SetWTitle( pictDocWindow, reply.sfFile.name );
  1145.  
  1146.             /* Show the window in its final size and position */
  1147.             ShowWindow( pictDocWindow );    
  1148.         }
  1149.         else
  1150.             /* Chosen file already open; just select it */
  1151.             SelectWindow( pictDocWindow );
  1152.     }
  1153.  
  1154.     return pictDocWindow;
  1155. }
  1156.  
  1157.  
  1158. /******************************************************************************\
  1159. * NAME & SYNOPSIS:
  1160. * CreatePictDoc: Create an empty Picture Document
  1161. *
  1162. * PARAMETERS:
  1163. * Rect      *pictureRect: Bounding rectangle of the image
  1164. * WindowPtr *retDoc:      Returns pointer to new Picture Document window
  1165. *
  1166. * DEFINITION:
  1167. * This routine creates a new Picture Document window and returns a pointer to
  1168. * it.  If the Picture Document couldn’t be created for some reason, then
  1169. * CreatePictDoc returns the error code, and the retDoc parameter is untouched.
  1170. *
  1171. * DESCRIPTION:
  1172. * Space for the window record is allocated before GetNewWindow is called so that]
  1173. * heap fragmentation is minimized.  Then, the window is initialized with
  1174. * GetNewWindow.  Then the vertical and horizontal scroll bars are created, and
  1175. * the Picture Document information record is allocated and initialized.  The
  1176. * last major task is to stagger the window into position, and the routines that
  1177. * are defined in WindowPositioner.c are used to do this.
  1178. *
  1179. * RETURN VALUES:
  1180. * Result: Error code if Picture Document window couldn’t be created
  1181. * retDoc: Pointer to the new Picture Document window, 
  1182. \******************************************************************************/
  1183.  
  1184. static OSErr CreatePictDoc(
  1185.     Rect       *pictureRect, /* Bounding rectangle of off-screen image */
  1186.     short      pictureDepth, /* Pixel depth of off-screen image */
  1187.     CTabHandle pictureClut,  /* Color table of off-screen image */
  1188.     WindowPtr  *retDoc)      /* Returns ptr to new Picture Document window */
  1189. {
  1190.     WindowPtr      docWindow;      /* Pointer to new Picture Document window */
  1191.     Ptr            windowStore;    /* Pointer to pre-allocated window rec */
  1192.     PictDocInfoHnd docInfo;        /* Handle to Picture Document information */
  1193.     PaletteHandle  pictureColors;  /* Palette with picture’s colors */
  1194.     GWorldPtr      newImage;       /* New off-screen image */
  1195.     ControlHandle  vScrollBar;     /* Vertical scroll bar */
  1196.     ControlHandle  hScrollBar;     /* Horizontal scroll bar */
  1197.     Rect           windowRect;     /* Destination rectangle of window */
  1198.     Point          windowBias;     /* Bias of Picture Document window */
  1199.     OSErr          error;
  1200.     ExceptionRec   exception;
  1201.     long           msgType;
  1202.     long           msgCode;
  1203.  
  1204.     docWindow = nil;
  1205.     docInfo = nil;
  1206.     windowStore = nil;
  1207.  
  1208.     /* Exception-handling code */
  1209.     if (ExceptionEntry( /*<*/&exception, /*<*/&msgType, /*<*/&msgCode ))
  1210.     {
  1211.         if (docWindow != nil)
  1212.             CloseWindow( docWindow );
  1213.         if (windowStore != nil)
  1214.             DisposPtr( windowStore );
  1215.         if (docInfo != nil)
  1216.             DisposHandle( (Handle)docInfo );
  1217.         return msgCode;
  1218.     }
  1219.  
  1220.     /* Pre-allocate the window record to avoid heap fragmentation */
  1221.     windowStore = NewPtrMargin( sizeof (WindowRecord), kAllocApp, !kAllocClr );
  1222.     if (windowStore == nil)
  1223.         Exception( &exception, 0, memFullErr );
  1224.  
  1225.     /* Create the off-screen graphics environment */
  1226.     if (pictureClut == nil)
  1227.         pictureClut = GetCTable( pictureDepth );
  1228.     error = NewGWorld( /*<*/&newImage, pictureDepth, pictureRect, pictureClut,
  1229.             nil, 0 );
  1230.     if (error != noErr)
  1231.         Exception( &exception, 0, error );
  1232.  
  1233.     /* Create a new Picture Document window */
  1234.     docWindow = GetNewCWindow( rPictDocWindID, windowStore,
  1235.             (WindowPtr)-1L );
  1236.     if (docWindow == nil)
  1237.         if (FailLowMemory( 0 ) || ResError() == memFullErr)
  1238.             Exception( &exception, 0, memFullErr );
  1239.         else if (ResError() == noErr || ResError() == resNotFound)
  1240.             Exception( &exception, 0, resNotFound );
  1241.     SetPort( docWindow );
  1242.  
  1243.     /* Give the window a palette with the window colors */
  1244.     pictureColors = NewPalette( (**pictureClut).ctSize + 1, pictureClut,
  1245.             pmTolerant, 0 );
  1246.     SetPalette( docWindow, pictureColors, true );
  1247.  
  1248.     /* Create the horizontal and vertical scroll bars */
  1249.     vScrollBar = GetNewControl( rScrollBarID, docWindow );
  1250.     if (vScrollBar == nil)
  1251.         if (FailLowMemory( 0 ) || ResError() == memFullErr)
  1252.             Exception( &exception, 0, memFullErr );
  1253.         else if (ResError() == noErr || ResError() == resNotFound)
  1254.             Exception( &exception, 0, resNotFound );
  1255.     hScrollBar = GetNewControl( rScrollBarID, docWindow );
  1256.     if (hScrollBar == nil)
  1257.         if (FailLowMemory( 0 ) || ResError() == memFullErr)
  1258.             Exception( &exception, 0, memFullErr );
  1259.         else if (ResError() == noErr || ResError() == resNotFound)
  1260.             Exception( &exception, 0, resNotFound );
  1261.  
  1262.     /* Allocate a new Picture Document information record */
  1263.     docInfo = (PictDocInfoHnd)NewHandleMargin( sizeof (PictDocInfoRec),
  1264.             kAllocApp, !kAllocClr );
  1265.     if (docInfo == nil)
  1266.         Exception( &exception, rMemErrMessages, kMemErrOpenDocMsg );
  1267.     (**docInfo).image = newImage;
  1268.     (**docInfo).vScrollBar = vScrollBar;
  1269.     (**docInfo).hScrollBar = hScrollBar;
  1270.     (**docInfo).destRect = newImage->portRect;
  1271.     (**docInfo).fileRef = 0;
  1272.     SetWRefCon( docWindow, (long)docInfo );
  1273.  
  1274.     /* Stagger the window onto the screen */
  1275.     windowBias = CalcWindowBias( documentProc,
  1276.             ((WindowPeek)docWindow)->goAwayFlag );
  1277.     SetRect( /*<*/&windowRect, 0, 0, pictureRect->right + kScrollBarWidth - 1,
  1278.             pictureRect->bottom + kScrollBarWidth - 1 );
  1279.     LocalToGlobal( /*◊*/&topLeft( windowRect ) );
  1280.     LocalToGlobal( /*◊*/&botRight( windowRect ) );
  1281.     PositionScreenRect( /*◊*/&windowRect,
  1282.             kParentScreenPos, kStaggerPos, NextPictDocWindow( nil ),
  1283.             windowBias.h, windowBias.v );
  1284.     MoveWindow( docWindow, windowRect.left, windowRect.top, false );
  1285.     SizeWindow( docWindow, windowRect.right - windowRect.left,
  1286.             windowRect.bottom - windowRect.top, false );
  1287.     ResizePictDoc( docWindow );
  1288.  
  1289.     /* Identify this window for later */
  1290.     ((WindowPeek)docWindow)->windowKind = kDocWKind;
  1291.  
  1292.     *retDoc = docWindow;
  1293.     return noErr;
  1294. }
  1295.  
  1296.  
  1297. /******************************************************************************\
  1298. * NAME & SYNOPSIS:
  1299. * DoSaveAsPictDoc: Save a new Picture Document
  1300. *
  1301. * PARAMETERS:
  1302. * See PictDocument.h
  1303. *
  1304. * DEFINITION:
  1305. * See PictDocument.h
  1306. *
  1307. * DESCRIPTION:
  1308. * The user is asked for a file name for the new Picture Document file through
  1309. * Standard File.  If the user specifies the file name and location and chooses
  1310. * OK, DoSaveAsPictDoc creates a new PICT file if it didn’t exist already, and
  1311. * then it opens the PICT file for writing.  The Picture Document’s off-screen
  1312. * GrafPort is set as the current port, and then OpenFilePicture (defined below)
  1313. * is called which opens a new picture for spooling.  Then the off-screen image
  1314. * is CopyBits’ed on top of itself which spools the off-screen image to the PICT
  1315. * file.  Then the spooled picture is closed, which completes the writing of the
  1316. * PICT file.
  1317. *
  1318. * RETURN VALUES:
  1319. * See PictDocument.h
  1320. \******************************************************************************/
  1321.  
  1322. void DoSaveAsPictDoc(
  1323.     WindowPtr docWindow) /* Pointer to Picture Document window being saved */
  1324. {
  1325.     StandardFileReply reply;       /* Location and name of new PICT file */
  1326.     Str255            docTitle;    /* Holds the existing name of the document */
  1327.     PictDocInfoHnd    docInfo;     /* Handle to Picture Document information */
  1328.     CGrafPtr          image;       /* Points to document’s image */
  1329.     CGrafPtr          savedPort;   /* Saves current port for later restoring */
  1330.     GDHandle          savedDevice; /* Saves current GDevice for restoring */
  1331.     short             pictFileRef; /* File reference number of PICT file */
  1332.     OSErr             error;
  1333.     ExceptionRec      exception;
  1334.     long              msgType;
  1335.     long              msgCode;
  1336.  
  1337.     pictFileRef = 0;
  1338.  
  1339.     /* Exception-handling code */
  1340.     if (ExceptionEntry( /*<*/&exception, /*<*/&msgType, /*<*/&msgCode ))
  1341.     {
  1342.         if (pictFileRef != 0)
  1343.             (void)FSClose( pictFileRef );
  1344.         return;
  1345.     }
  1346.  
  1347.     /* Get the name and location of the new file from the user */
  1348.     GetWTitle( docWindow, /*<*/docTitle );
  1349.     StandardPutFile( "\P", docTitle, &reply );
  1350.     if (reply.sfGood)
  1351.     {
  1352.         /* Create the new PICT file if it doesn’t already exist */
  1353.         if (!reply.sfReplacing)
  1354.         {
  1355.             error = HCreate( reply.sfFile.vRefNum, reply.sfFile.parID,
  1356.                     reply.sfFile.name, kFileCreator, kFileType );
  1357.             if (error != noErr)
  1358.                 Exception( &exception, rMiscErrMessages, kMiscErrUnknownMsg );
  1359.         }
  1360.  
  1361.         /* Open the new PICT file */
  1362.         error = HOpen( reply.sfFile.vRefNum, reply.sfFile.parID,
  1363.                 reply.sfFile.name, fsCurPerm, /*<*/&pictFileRef );
  1364.         if (error != noErr)
  1365.             Exception( &exception, rMiscErrMessages, kMiscErrUnknownMsg );
  1366.  
  1367.         /* Get the Picture Document’s image and make it the current port */
  1368.         docInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
  1369.         image = (**docInfo).image;
  1370.         GetGWorld( /*<*/&savedPort, /*<*/&savedDevice );
  1371.         SetGWorld( image, nil );
  1372.  
  1373.         /* Create a PICT file with the Picture Document’s image */
  1374.         error = OpenFilePicture( pictFileRef, &image->portRect );
  1375.         if (error != noErr)
  1376.             Exception( &exception, rMiscErrMessages, kMiscErrUnknownMsg );
  1377.         CopyBits( &((GrafPtr)image)->portBits, &((GrafPtr)image)->portBits,
  1378.                 &image->portRect, &image->portRect,
  1379.                 srcCopy, nil );
  1380.         error = CloseFilePicture();
  1381.         if (error != noErr)
  1382.             Exception( &exception, rMiscErrMessages, kMiscErrUnknownMsg );
  1383.  
  1384.         /* Restore the current port */
  1385.         SetGWorld( savedPort, savedDevice );
  1386.  
  1387.         /* Close the existing PICT file if any */
  1388.         if ((**docInfo).fileRef != 0)
  1389.             (void)FSClose( (**docInfo).fileRef );
  1390.  
  1391.         /* Remember the new PICT file’s reference number */
  1392.         (**docInfo).fileRef = pictFileRef;
  1393.         (**docInfo).file = reply.sfFile;
  1394.  
  1395.         /* Set the title of the window to the title of the file */
  1396.         SetWTitle( docWindow, reply.sfFile.name );
  1397.     }
  1398. }
  1399.  
  1400.  
  1401. /******************************************************************************\
  1402. * NAME & SYNOPSIS:
  1403. * DoClosePictDoc: Close a Picture Document window
  1404. *
  1405. * PARAMETERS:
  1406. * See PictDocument.h
  1407. *
  1408. * DEFINITION:
  1409. * See PictDocument.h
  1410. *
  1411. * DESCRIPTION:
  1412. * The off-screen image is disposed of and the Picture Document’s PICT file is
  1413. * closed.  Then all the memory associated with the window is disposed of.
  1414. *
  1415. * RETURN VALUES:
  1416. * See PictDocument.h
  1417. \******************************************************************************/
  1418.  
  1419. Boolean DoClosePictDoc(
  1420.     WindowPtr docWindow) /* Ptr to Picture Document window being closed */
  1421. {
  1422.     PictDocInfoHnd docInfo; /* Handle to Picture Document info record */
  1423.  
  1424.     docInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
  1425.     if (docInfo != nil)
  1426.     {
  1427.         /* Get rid of the off-screen CGrafPort */
  1428.         if ((**docInfo).image != nil)
  1429.             DisposeGWorld( (**docInfo).image );
  1430.  
  1431.         /* Close the Picture File */
  1432.         if ((**docInfo).fileRef != 0)
  1433.             (void)FSClose( (**docInfo).fileRef );
  1434.  
  1435.         /* Dispose of the Picture Document information record */
  1436.         DisposHandle( (Handle)docInfo );
  1437.     }
  1438.  
  1439.     /* Close and dispose of the Picture Document window */
  1440.     DisposePalette( GetPalette( docWindow ) );
  1441.     CloseWindow( docWindow );
  1442.     DisposPtr( (Ptr)docWindow );
  1443.  
  1444.     return true;
  1445. }
  1446.  
  1447.  
  1448. /******************************************************************************\
  1449. * NAME & SYNOPSIS:
  1450. * GetPictureInfo: Get basic information about a PICT document
  1451. *
  1452. * PARAMETERS:
  1453. * short          pictFileRef:  File reference number of PICT file
  1454. * PictureInfoRec *retPictRect: Returns information on picture
  1455. *
  1456. * DEFINITION:
  1457. * GetPictureInfo examines the PICT file whose file reference number is specified
  1458. * in the pictFileRef parameter to get the picFrame of the picture, and the pixel
  1459. * depth and color table of the deepest PixMap in the picture.  This information
  1460. * is returned in the retPictInfo parameter.
  1461. *
  1462. * DESCRIPTION:
  1463. * GetPictureInfo uses picture spooling to get the information about the picture
  1464. * in the specified PICT file.  A temporary CGrafPort or GrafPort is set up just
  1465. * so that no existing ports are touched.  Then a CQDProcs or QDProcs record is
  1466. * set up so that picture data comes from the PICT file instead of memory, and so
  1467. * that any CopyBits calls within the picture get routed to the InfoBits routine
  1468. * (defined below).  Then the standard picture spooling mechanism is used to read
  1469. * the PICT file.  The clip region is set to nothing to prevent drawing——only the
  1470. * information about the PixMaps is needed, and InfoBits gets that.
  1471. *
  1472. * RETURN VALUES:
  1473. * Result:      Error code if anything went wrong
  1474. * retPictInfo: Size of picture, and depth and color table of deepest PixMap in
  1475. *              the picture.
  1476. \******************************************************************************/
  1477.  
  1478. static OSErr GetPictureInfo(
  1479.     short          pictFileRef,  /* File reference number of PICT file */
  1480.     PictureInfoRec *retPictInfo) /* Returns information on picture */
  1481. {
  1482.     CGrafPort    tempPort;     /* Temp port used for “Drawing” pict */
  1483.     Boolean      tempPortOpen; /* True if tempPort has been opened */
  1484.     GrafPtr      savedPort;    /* Saves current port for restoring */
  1485.     CQDProcs     customProcs;  /* Custom GrafProcs */
  1486.     PicHandle    spoolPict;    /* Picture used for spooling */
  1487.     Rect         emptyRect;    /* Used to set empty clipping region */
  1488.     long         dataLen;      /* Number of bytes of data to read */
  1489.     long         qdVersion;    /* Version number of QuickDraw */
  1490.     OSErr        error;
  1491.     ExceptionRec exception;
  1492.     long         errorCode;
  1493.  
  1494.     error = noErr;
  1495.     tempPortOpen = false;
  1496.     savedPort = nil;
  1497.     spoolPict = nil;
  1498.  
  1499.     /* Exception-handling code */
  1500.     if (ExceptionEntry( /*<*/&exception, nil, /*<*/&errorCode ))
  1501.     {
  1502.         SetPort( savedPort );
  1503.         if (tempPortOpen)
  1504.             ClosePort( (GrafPtr)&tempPort );
  1505.         if (spoolPict != nil)
  1506.             DisposHandle( (Handle)spoolPict );
  1507.         return errorCode;
  1508.     }
  1509.  
  1510.     /* Get the current version of QuickDraw */
  1511.     (void)Gestalt( gestaltQuickdrawVersion, /*<*/&qdVersion );
  1512.  
  1513.     /* Save current port so that we can restore it later */
  1514.     GetPort( /*<*/&savedPort );
  1515.  
  1516.     /* Open the temporary CGrafPort or GrafPort, depending on QD version */
  1517.     if (qdVersion >= gestalt8BitQD)
  1518.         OpenCPort( &tempPort );
  1519.     else
  1520.         OpenPort( (GrafPtr)&tempPort );
  1521.     tempPortOpen = true;
  1522.  
  1523.     /* Don’t want anything drawn */
  1524.     SetRect( /*<*/&emptyRect, 0, 0, 0, 0 );
  1525.     ClipRect( &emptyRect );
  1526.  
  1527.     /* Set up the GrafProcs for spooling */
  1528.     if (qdVersion >= gestalt8BitQD)
  1529.         SetStdCProcs( /*<*/&customProcs );
  1530.     else
  1531.         SetStdProcs( /*<*/(QDProcsPtr) &customProcs );
  1532.     customProcs.bitsProc = (Ptr)InfoBits;
  1533.     customProcs.getPicProc = (Ptr)FileGetPic;
  1534.  
  1535.     /* Install the custom GrafProcs into the tempPort */
  1536.     tempPort.grafProcs = &customProcs;
  1537.  
  1538.     /* Create the picture used for spooling */
  1539.     spoolPict = (PicHandle)NewHandleMargin( sizeof (Picture), kAllocApp,
  1540.             !kAllocClr );
  1541.     if (spoolPict == nil)
  1542.         Exception( &exception, 0, memFullErr );
  1543.  
  1544.     /* Skip the PICT file header */
  1545.     error = SetFPos( pictFileRef, fsFromStart, 512L );
  1546.     if (error != noErr)
  1547.         Exception( &exception, 0, error );
  1548.  
  1549.     /* Read the picture header into spoolPict */
  1550.     dataLen = sizeof (Picture);
  1551.     error = FSRead( pictFileRef, /*◊*/&dataLen, (Ptr) *spoolPict );
  1552.     if (error != noErr)
  1553.         Exception( &exception, 0, error );
  1554.  
  1555.     /* Spool the picture in */
  1556.     gPictureInfo.colors = nil;
  1557.     gPictureInfo.depth = 0;
  1558.     gPictureInfo.frame = (**spoolPict).picFrame;
  1559.     gPictFileRef = pictFileRef;
  1560.     DrawPicture( spoolPict, &gPictureInfo.frame );
  1561.  
  1562.     /* Clean up after myself */
  1563.     SetPort( savedPort );
  1564.     ClosePort( (GrafPtr)&tempPort );
  1565.     tempPortOpen = false;
  1566.     DisposHandle( (Handle)spoolPict );
  1567.  
  1568.     /* Return picture information */
  1569.     *retPictInfo = gPictureInfo;
  1570.     return noErr;
  1571.     }
  1572.  
  1573.  
  1574. /******************************************************************************\
  1575. * NAME & SYNOPSIS:
  1576. * InfoBits: GrafProc to get information about bit/pixel maps
  1577. *
  1578. * PARAMETERS:
  1579. * BitMapPtr srcBits:  Pointer to bit/pixel map being transferred
  1580. * Rect      *srcRect: Source rectangle of CopyBits call; ignored
  1581. * Rect      *dstRect: Destination rectangle of CopyBits call; ignored
  1582. * short     mode:     Transfer mode; ignored
  1583. * RgnHandle maskRgn:  Region to mask CopyBits; ignored
  1584. *
  1585. * DEFINITION:
  1586. * InfoBits is a custom QuickDraw graphics procedure which is used to find the
  1587. * depth and color table of the deepest PixMap in the picture that’s being drawn.
  1588. * This information is returned in the gPictureInfo global variable.
  1589. *
  1590. * DESCRIPTION:
  1591. * InfoBits is called every time a CopyBits call is found in a picture that’s
  1592. * being drawn.  It first checks to see if the CopyBits call is for a PixMap or
  1593. * a BitMap.  If it’s a BitMap, the most significant bit of rowBytes is clear; if
  1594. * it’s a PixMap, the most significant byte of rowBytes is set.  If it’s a
  1595. * PixMap, its depth is compared against the depth of any previous PixMap that
  1596. * has been seen in the picture.  If it’s deeper, then the depth and color table
  1597. * of the PixMap is saved.  Otherwise, the PixMap is ignored.
  1598. *
  1599. * If srcBits is a BitMap and no PixMaps have yet been seen in the picture, then
  1600. * InfoBits remembers a depth of 1 bit per pixel and no color table.
  1601. *
  1602. * RETURN VALUES:
  1603. * gPictureInfo: Depth and color table of deepest PixMap in a picture.
  1604. \******************************************************************************/
  1605.  
  1606. static pascal void InfoBits(
  1607.     BitMapPtr srcBits,  /* Pointer to bit/pixel map being transferred */
  1608.     Rect      *srcRect, /* Source rectangle of CopyBits call; ignored */
  1609.     Rect      *dstRect, /* Destination rectangle of CopyBits call; ignored */
  1610.     short     mode,     /* Transfer mode; ignored */
  1611.     RgnHandle maskRgn)  /* Region to mask CopyBits; ignored */
  1612. {
  1613. #pragma unused (srcRect,dstRect,mode,maskRgn)
  1614.     PixMapPtr srcPix; /* Handle to the picture’s PixMap */
  1615.  
  1616.     if (srcBits->rowBytes & 0x8000)
  1617.     {
  1618.         /* srcBits is a pixel map, get depth and color table */
  1619.         srcPix = (PixMapPtr)srcBits;
  1620.  
  1621.         /* Only save info if this pixel map is deepest seen so far */
  1622.         if (srcPix->pixelSize > gPictureInfo.depth)
  1623.         {
  1624.             gPictureInfo.depth = srcPix->pixelSize;
  1625.             if (srcPix->pixelType != RGBDirect)
  1626.             {
  1627.                 if (gPictureInfo.colors != nil)
  1628.                     DisposHandle( (Handle)gPictureInfo.colors );
  1629.                 gPictureInfo.colors = srcPix->pmTable;
  1630.                 HandToHand( (Handle *)&gPictureInfo.colors );
  1631.             }
  1632.         }
  1633.     }
  1634.     else
  1635.     {
  1636.         /* srcBits is a bit map, has depth of 1 and B&W color table */
  1637.         if (gPictureInfo.depth == 0)
  1638.         {
  1639.             gPictureInfo.depth = 1;
  1640.             if (gPictureInfo.colors != nil)
  1641.                 DisposHandle( (Handle)gPictureInfo.colors );
  1642.             gPictureInfo.colors = nil;
  1643.         }
  1644.     }
  1645. }
  1646.  
  1647.  
  1648. /******************************************************************************\
  1649. * NAME & SYNOPSIS:
  1650. * DrawFilePicture: Read a PICT file into the current GrafPort
  1651. *
  1652. * PARAMETERS:
  1653. * short pictFileRef: File reference number of PICT file to open
  1654. * Rect  *destRect:   Rectangle to draw the picture into
  1655. *
  1656. * DEFINITION:
  1657. * This routine is used to read the picture data from the PICT file specified by
  1658. * pictFileRef and draw the picture into the rectangle specified by the destRect
  1659. * parameter in the current GrafPort.  It’s just like DrawPicture, except the
  1660. * file reference number of a PICT file is specified instead of a PicHandle.
  1661. *
  1662. * DESCRIPTION:
  1663. * DrawFilePicture works by using the picture spooling method described in Inside
  1664. * Macintosh V on pages 87 through 90.  In this method, the getPicProc GrafProc is
  1665. * replaced by my routine called FileGetPic which reads PICT information from
  1666. * disk.  By doing this, the DrawPicture command effectively reads PICT files.
  1667. *
  1668. * After setting up the GrafProcs record, this routine installs the GrafProcs
  1669. * into the current port.  Then, a temporary picture is manually allocated and
  1670. * placed into spoolPict.  This is essentially a placeholder to hold the picture
  1671. * header (which is different from the 512-byte PICT file header) and is always
  1672. * the size of the Picture record type without any picture data.
  1673. *
  1674. * The PICT file is opened using HOpen.  HOpen is documented in the File Manager
  1675. * chapter of Inside Macintosh VI, but it still works on pre-7.0 machines because
  1676. * it’s just glue code that calls PBHOpen.  Because of the way the File Manager
  1677. * works, this routine also works on MFS volumes.  This routine is preferable to
  1678. * FSOpen because HOpen allows you to specify access permissions, and because it
  1679. * lets you specify a file by the more reliable volume reference number,
  1680. * directory ID, and file name rather than by working directory reference number
  1681. * and file name.  I’m using the FSSpec record to hold file information, so
  1682. * specifying a file like this is especially convenient.  If this were a 7.0-only
  1683. * application, I’d call FSpOpen instead of HOpen because FSpOpen takes an FSSpec
  1684. * record directly.
  1685. *
  1686. * After the PICT file is opened, SetFPos is called to skip the first 512 bytes
  1687. * of the file.  The first 512 bytes of a PICT file is called the PICT file
  1688. * header (not to be confused with the picture header, which contains the picSize
  1689. * and picFrame fields of the Picture type) and holds application-specific
  1690. * information.  This application doesn’t care what’s there so it just skips it.
  1691. * The picture header follows the PICT file header and is read into spoolPict.
  1692. *
  1693. * Finally (almost), DrawPicture is called.  Because this routine replaced the
  1694. * current port’s GrafProcs with my own, DrawPicture gets the picture information
  1695. * from the PICT file rather than from spoolPict.  It draws the picture into the
  1696. * current port.
  1697. *
  1698. * RETURN VALUES:
  1699. * Result:   Returns an error code.
  1700. * destRect: Returns the picFrame of the picture in the PICT file
  1701. \******************************************************************************/
  1702.  
  1703. static OSErr DrawFilePicture(
  1704.     short pictFileRef, /* File reference number of PICT file to open */
  1705.     Rect  *destRect)   /* Rectangle to draw the picture into */
  1706. {
  1707.     GrafPtr      currPort;    /* Ptr to current port used for Drawing pict */
  1708.     CQDProcs     customProcs; /* Custom GrafProcs */
  1709.     QDProcsPtr   savedProcs;  /* Current GrafPort’s QD procs; used to restore */
  1710.     PicHandle    spoolPict;   /* Picture used for spooling */
  1711.     long         dataLen;     /* Number of bytes of data to read */
  1712.     long         qdVersion;   /* Version number of QuickDraw */
  1713.     OSErr        error;
  1714.     ExceptionRec exception;
  1715.     long         errorCode;
  1716.  
  1717.     currPort = nil;
  1718.     spoolPict = nil;
  1719.     savedProcs = nil;
  1720.     error = noErr;
  1721.  
  1722.     /* Exception-handling code */
  1723.     if (ExceptionEntry( /*<*/&exception, nil, /*<*/&errorCode ))
  1724.     {
  1725.         if (spoolPict != nil)
  1726.             DisposHandle( (Handle)spoolPict );
  1727.         if (savedProcs != nil)
  1728.             currPort->grafProcs = savedProcs;
  1729.         return errorCode;
  1730.     }
  1731.  
  1732.     /* Get the current version of QuickDraw */
  1733.     (void)Gestalt( gestaltQuickdrawVersion, /*<*/&qdVersion );
  1734.  
  1735.     /* Get a pointer to the current port and determine its type */
  1736.     GetPort( /*<*/&currPort );
  1737.  
  1738.     /* Initialize the GrafProcs for reading from a PICT file */
  1739.     if (qdVersion >= gestalt8BitQD)
  1740.         SetStdCProcs( /*<*/&customProcs );
  1741.     else
  1742.         SetStdProcs( /*<*/(QDProcsPtr) &customProcs );
  1743.     customProcs.getPicProc = (Ptr)FileGetPic;
  1744.  
  1745.     /* Install the custom GrafProcs into the tempPort */
  1746.     savedProcs = currPort->grafProcs;
  1747.     currPort->grafProcs = (QDProcsPtr)&customProcs;
  1748.  
  1749.     /* Create the temporary picture used for spooling */
  1750.     spoolPict = (PicHandle)NewHandleMargin( sizeof (Picture),
  1751.             kAllocApp, !kAllocClr );
  1752.     if (spoolPict == nil)
  1753.         Exception( &exception, 0, memFullErr );
  1754.  
  1755.     /* Skip the 512-byte PICT file header */
  1756.     error = SetFPos( pictFileRef, fsFromStart, kPictFileHeaderSize );
  1757.     if (error != noErr)
  1758.         Exception( &exception, 0, error );
  1759.  
  1760.     /* Read the picture header (not PICT file header) into spoolPict */
  1761.     dataLen = sizeof (Picture);
  1762.     error = FSRead( pictFileRef, /*◊*/&dataLen, (Ptr)*spoolPict );
  1763.     if (error != noErr)
  1764.         Exception( &exception, 0, error );
  1765.  
  1766.     /* Spool the picture in */
  1767.     gPictFileRef = pictFileRef;
  1768.     DrawPicture( spoolPict, destRect );
  1769.  
  1770.     /* Clean up after myself */
  1771.     DisposHandle( (Handle)spoolPict );
  1772.     currPort->grafProcs = savedProcs;
  1773.  
  1774.     return noErr;
  1775. }
  1776.  
  1777.  
  1778. /******************************************************************************\
  1779. * NAME & SYNOPSIS:
  1780. * FileGetPic: Get QuickDraw picture information from a file
  1781. *
  1782. * PARAMETERS:
  1783. * Ptr   dataPtr:   Points to location that picture data should be placed
  1784. * short byteCount: Number of bytes of picture data to read
  1785. *
  1786. * DEFINITION:
  1787. * This routine is a custom QuickDraw graphics procedure which replaces the
  1788. * standard GetPicProc.  QuickDraw graphics procedures are routines that
  1789. * QuickDraw calls to do almost everything that it does, like drawing rectangles,
  1790. * or getting picture data.  They can be replaced by setting up a QDProcs record
  1791. * and setting the appropriate field to point to the custom QuickDraw graphics
  1792. * procedure.  In this case, the getPicProc field is set to point to FileGetPic
  1793. * so that any DrawPicture call within the GrafPort causes FileGetPic to be
  1794. * called to get picture data.  Instead of getting picture data from memory,
  1795. * FileGetPic gets picture data from the disk file whose file reference number is
  1796. * stored in the gPictFileRef global variable.
  1797. *
  1798. * DESCRIPTION:
  1799. * FileGetPic simply calls FSRead to read data from the file whose file reference
  1800. * number is stored in gPictFileRef into the area pointed to by dataPtr, which is
  1801. * an area of memory that’s set up by QuickDraw to receive picture data.  The
  1802. * number of bytes of picture data that QuickDraw wants is specified by the
  1803. * byteCount parameter.
  1804. *
  1805. * RETURN VALUES:
  1806. * None
  1807. \******************************************************************************/
  1808.  
  1809. static pascal void FileGetPic(
  1810.     Ptr   dataPtr,   /* Points to location that picture data should be placed */
  1811.     short byteCount) /* Number of bytes of picture data to read */
  1812. {
  1813.     long longCount; /* Number of bytes to read from the PICT file */
  1814.  
  1815.     longCount = byteCount;
  1816.     (void)FSRead( gPictFileRef, /*◊*/&longCount, dataPtr );
  1817. }
  1818.  
  1819.  
  1820. /******************************************************************************\
  1821. * NAME & SYNOPSIS:
  1822. * OpenFilePicture: Open a picture for spooling to a file
  1823. *
  1824. * PARAMETERS:
  1825. * short pictFileRef: File reference number of PICT file to write to
  1826. * Rect  *sourceRect: Rectangle to draw the picture from
  1827. *
  1828. * DEFINITION:
  1829. * Normally, when you create a picture, you call OpenPicture, draw, then call
  1830. * ClosePicture.  But if you can create a PICT file in a similar way by calling
  1831. * OpenFilePicture and CloseFilePicture.  Almost like you would normally, you
  1832. * should set up the current port however you want it, call OpenFilePicture,
  1833. * use QuickDraw routines to draw, then call CloseFilePicture when you’re done.
  1834. * But before you call OpenFilePicture, you must open the PICT file for writing,
  1835. * and pass the file’s reference number in the pictFileRef parameter.  Once
  1836. * CloseFilePicture is called, the PICT file is complete.
  1837. *
  1838. * DESCRIPTION:
  1839. * OpenFilePicture installs the FilePutPic custom graphics procedure by setting
  1840. * up a QDprocs record with the default pointers, and then setting the putPicProc
  1841. * field to point to the FilePutPic function.  This QDProcs record is then
  1842. * installed into the current GrafPort.  Then, an empty picture is allocated just
  1843. * to have something to pass to the real OpenPicture routine.  Then the 512-byte
  1844. * PICT file header is written.  This program doesn’t do anything with those 512
  1845. * bytes, so only zeroes are written.  Also, the ten-byte picture header is
  1846. * written with zeroes for now; it’ll get the real picture header after the PICT
  1847. * file data is written.  Finally, the global variables are set up so that
  1848. * FilePutPic can reach them.
  1849. *
  1850. * RETURN VALUES:
  1851. * Result: Error code of any error that occurs.
  1852. \******************************************************************************/
  1853.  
  1854. static OSErr OpenFilePicture(
  1855.     short pictFileRef, /* File reference number of PICT file to open */
  1856.     Rect  *sourceRect) /* Rectangle to draw the picture from */
  1857. {
  1858.     GrafPtr      currPort;    /* Ptr to current port used for Drawing pict */
  1859.     long         dataLen;     /* Number of bytes of data to write */
  1860.     short        zeroData;    /* Equal to 0; used to write PICT file header */
  1861.     short        count;       /* Generic counter */
  1862.     OSErr        error;
  1863.     ExceptionRec exception;
  1864.     long         errorCode;
  1865.  
  1866.     currPort = nil;
  1867.     gSpoolPicture = nil;
  1868.     gSavedProcs = nil;
  1869.     error = noErr;
  1870.  
  1871.     /* Exception-handling code */
  1872.     if (ExceptionEntry( /*<*/&exception, nil, /*<*/&errorCode ))
  1873.     {
  1874.         if (gSpoolPicture != nil)
  1875.             DisposHandle( (Handle)gSpoolPicture );
  1876.         if (gSavedProcs != nil)
  1877.             currPort->grafProcs = gSavedProcs;
  1878.         return errorCode;
  1879.     }
  1880.  
  1881.     /* Get a pointer to the current port */
  1882.     GetPort( /*<*/&currPort );
  1883.  
  1884.     /* Initialize the GrafProcs for reading from a PICT file */
  1885.     if (currPort->portBits.rowBytes & 0x8000)
  1886.         SetStdCProcs( /*<*/&gCustomProcs );
  1887.     else
  1888.         SetStdProcs( /*<*/(QDProcsPtr)&gCustomProcs );
  1889.     gCustomProcs.putPicProc = (Ptr)FilePutPic;
  1890.  
  1891.     /* Install the custom GrafProcs into the tempPort */
  1892.     gSavedProcs = currPort->grafProcs;
  1893.     currPort->grafProcs = (QDProcsPtr)&gCustomProcs;
  1894.  
  1895.     /* Create the temporary picture used for spooling */
  1896.     gSpoolPicture = (PicHandle)NewHandleMargin( sizeof (Picture),
  1897.             kAllocApp, kAllocClr );
  1898.     if (gSpoolPicture == nil)
  1899.         Exception( &exception, 0, memFullErr );
  1900.  
  1901.     /* Make sure we’re positioned at the beginning of the file */
  1902.     error = SetFPos( pictFileRef, fsFromStart, 0 );
  1903.     if (error != noErr)
  1904.         Exception( &exception, 0, error );
  1905.  
  1906.     /* Zero the 512-byte PICT file header */
  1907.     dataLen = sizeof (short);
  1908.     zeroData = 0;
  1909.     for (count = 0; count < (kPictFileHeaderSize + sizeof (Picture))
  1910.             / sizeof (short); ++count)
  1911.     {
  1912.         error = FSWrite( pictFileRef, /*◊*/&dataLen, &zeroData );
  1913.         if (error != noErr)
  1914.             Exception( &exception, 0, error );
  1915.     }
  1916.  
  1917.     /* Begin spooling the picture out */
  1918.     gPictFileRef = pictFileRef;
  1919.     gPictureSize = sizeof (Picture);
  1920.     gSpoolPicture = nil;
  1921.     gSpoolPicture = OpenPicture( sourceRect );
  1922.  
  1923.     return noErr;
  1924. }
  1925.  
  1926.  
  1927. /******************************************************************************\
  1928. * NAME & SYNOPSIS:
  1929. * CloseFilePicture: Close a spooled picture (don’t close the PICT file)
  1930. *
  1931. * PARAMETERS:
  1932. * None
  1933. *
  1934. * DEFINITION:
  1935. * ClosePictureFile is called to complete the writing of a PICT file.  It’s
  1936. * called the same way you’d call ClosePicture.
  1937. *
  1938. * DESCRIPTION:
  1939. * ClosePicture is called to close the currently-open picture, then the picture
  1940. * header is written with the final values.  Because the temporary picture isn’t
  1941. * needed anymore, KillPicture is called to get rid of it.  Finally, the current
  1942. * port’s graphics procedures are restored to what they were before
  1943. * OpenFilePicture was called.
  1944. *
  1945. * RETURN VALUES:
  1946. * Result: Error code of any error that occurs.
  1947. \******************************************************************************/
  1948.  
  1949. static OSErr CloseFilePicture()
  1950. {
  1951.     long         dataLen;  /* Number of bytes of data to write */
  1952.     GrafPtr      currPort; /* Pointer to the current GrafPort */
  1953.     OSErr        error;
  1954.     ExceptionRec exception;
  1955.     long         errorCode;
  1956.  
  1957.     error = noErr;
  1958.  
  1959.     /* Exception-handling code */
  1960.     if (ExceptionEntry( /*<*/&exception, nil, /*<*/&errorCode ))
  1961.     {
  1962.         return errorCode;
  1963.     }
  1964.  
  1965.     /* Close the spooled picture */
  1966.     ClosePicture();
  1967.  
  1968.     /* Make sure we’re positioned just past the PICT file header */
  1969.     error = SetFPos( gPictFileRef, fsFromStart, kPictFileHeaderSize );
  1970.     if (error != noErr)
  1971.         Exception( &exception, 0, error );
  1972.  
  1973.     /* Write the picture header (NOT the PICT file header!) */
  1974.     dataLen = sizeof (Picture);
  1975.     error = FSWrite( gPictFileRef, /*◊*/&dataLen, (Ptr)*gSpoolPicture );
  1976.     if (error != noErr)
  1977.         Exception( &exception, 0, error );
  1978.  
  1979.     /* Dispose of the picture */
  1980.     KillPicture( gSpoolPicture );
  1981.  
  1982.     /* Restore the previous graphics procedures */
  1983.     GetPort( /*<*/&currPort);
  1984.     currPort->grafProcs = gSavedProcs;
  1985. }
  1986.  
  1987.  
  1988. /******************************************************************************\
  1989. * NAME & SYNOPSIS:
  1990. * FilePutPic: Write QuickDraw picture information to PICT file
  1991. *
  1992. * PARAMETERS:
  1993. * Ptr   dataPtr:   Points to location of picture data
  1994. * short byteCount: Number of bytes of picture data to write
  1995. *
  1996. * DEFINITION:
  1997. * This routine is a custom QuickDraw graphics procedure which replaces the
  1998. * standard PutPicProc.  QuickDraw graphics procedures are routines that
  1999. * QuickDraw calls to do almost everything that it does, like drawing rectangles,
  2000. * or getting picture data.  They can be replaced by setting up a QDProcs record
  2001. * and setting the appropriate field to point to the custom QuickDraw graphics
  2002. * procedure.  In this case, the getPicProc field is set to point to FileGetPic
  2003. * so that any drawing call within the GrafPort while a picture is open causes
  2004. * FilePutPic to be called to write picture data.  Instead of writing picture
  2005. * data to memory, FileGetPic writes picture data to the disk file whose file
  2006. * reference number is stored in the gPictFileRef global variable.
  2007. *
  2008. * DESCRIPTION:
  2009. * FileGetPic calls FSWrite to write data t=o the file whose file reference
  2010. * number is stored in gPictFileRef from the area pointed to by dataPtr, which is
  2011. * an area of memory that’s set up by QuickDraw to hold picture data.  The
  2012. * number of bytes of picture data that QuickDraw has is specified by the
  2013. * byteCount parameter.  Also, the picSize field of gSpoolPicture is updated so
  2014. * that QuickDraw can keep track of the current size of the picture.
  2015. *
  2016. * RETURN VALUES:
  2017. * None
  2018. \******************************************************************************/
  2019.  
  2020. static pascal void FilePutPic(
  2021.     Ptr   dataPtr,   /* Points to location of picture data */
  2022.     short byteCount) /* Number of bytes of picture data to write */
  2023. {
  2024.     long dataLen; /* Number of bytes to write the PICT file */
  2025.  
  2026.     dataLen = byteCount;
  2027.     gPictureSize += dataLen;
  2028.     (void)FSWrite( gPictFileRef, /*◊*/&dataLen, dataPtr );
  2029.     if (gSpoolPicture != nil)
  2030.         (**gSpoolPicture).picSize = gPictureSize;
  2031. }
  2032.